package icraf.carbon.core;

import static icraf.carbon.core.Zone.DataType.BELOWGROUND_EMISSION;
import static icraf.carbon.core.Zone.DataType.CARBONSTOCK;
import static icraf.carbon.core.Zone.DataType.COSTBENEFIT_CONVERSION;
import static icraf.carbon.core.Zone.DataType.LANDCOVER_CHANGE;
import static icraf.carbon.core.Zone.DataType.LANDUSE_SCENARIO;
import static icraf.carbon.core.Zone.DataType.NPV;
import static icraf.carbon.core.Zone.DataType.TPM_SCENARIO;
import icraf.carbon.core.Zone.DataType;
import icraf.carbon.model.ModelProject;
import icraf.common.util.FileExcelIO;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import jxl.read.biff.BiffException;
import jxl.write.WriteException;

//TODO: keep compatibility!! ver 1.0.x and 1.1.x!!
public class ProjectIO {

	public enum FileDataType {
		BELOWGROUND_EMISSION(DataType.BELOWGROUND_EMISSION),
		BELOWGROUND_E_FACTOR(DataType.BG_EMISSION_FACTOR),
		MODIF_EMISSION(DataType.MODIF_EMISSION),
		MODIF_E_FACTOR(DataType.MODIF_EMISSION_FACTOR),
		
		CARBONSTOCK(DataType.CARBONSTOCK), 
		COSTB_PRIVATE, COSTB_SOCIAL, 
		COSTBENEFIT_CONVERSION(DataType.COSTBENEFIT_CONVERSION), 
		COSTBENEFIT_TOTAL(DataType.COSTBENEFIT_TOTAL), 
		COSTBENEFIT_UNIT, ELIGIBILITY, 
		EMISSION(DataType.EMISSION), 
		GENERAL, LANDCOVER, 
		LANDCOVER_CHANGE(DataType.LANDCOVER_CHANGE), 
		NPV, NPV_PRIVATE, NPV_SOCIAL, OPCOST(DataType.OPCOST), 
		ZONE, TRANSITION_PROBABILITY_MATRIX(DataType.TPM_SCENARIO);

		private DataType dataType = null;

		FileDataType() {
		}

		FileDataType(DataType dataType) {
			this.dataType = dataType;
		}

		public boolean equalsLabel(String s) {
			return s.equalsIgnoreCase(getLabel()) || s.startsWith(getLabel());
		}

		public DataType getDataType() {
			return dataType;
		}

		public String getLabel() {
			return "#" + this.toString();
		}
	}

	final static String fileVersion0 = "1.0.x", fileVersion1 = "1.1.0";
	private static FileDataType outputData[] = { FileDataType.EMISSION,
			FileDataType.COSTBENEFIT_TOTAL, FileDataType.OPCOST };

	private static final String R = "\r\n", T = "\t", E = "=";

	private static String checkLabel(String str) {
		String strLabel[] = str.split(T);
		for (FileDataType m : FileDataType.values()) {
			if (m.equalsLabel(strLabel[0])) {
				// if (strLabel.length > 1)
				// return m.getLabel().concat(T).concat(strLabel[1]);
				// return m.getLabel();
				return str;
			}
		}
		return null;
	}

	private static String[][] convertStringToMatrix(String str) {
		String[] strLine = str.split(R);
		int maxCol = 0;
		for (String line : strLine) {
			maxCol = Math.max(maxCol, line.split(T).length);
		}
		String[][] strMatrix = new String[strLine.length][maxCol];
		for (int i = 0; i < strLine.length; i++) {
			String[] line = strLine[i].split(T);
			for (int j = 0; j < line.length; j++) {
				strMatrix[i][j] = line[j];
			}
		}
		return strMatrix;
	}

	public static Project duplicateProject(Project p) {
		String str = generateProjectString(p);
		Project pn = null;
		try {
			pn = loadData(new StringReader(str));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return pn;
	}

	public static String generateCalculationOutputString(Project p) {
		StringBuffer sb = new StringBuffer();
		for (FileDataType t : outputData) {
			if (t == FileDataType.COSTBENEFIT_TOTAL || t == FileDataType.OPCOST) {
				for (CostBenefitUnit cbu : p.getCostBenefitUnitList())
					sb.append(generateLandCoverMatrixAllZones(p, t, cbu));
			} else {
				sb.append(generateLandCoverMatrixAllZones(p, t, null));
			}
			sb.append(R);
		}
		return sb.toString();
	}

	private static String generateLandCoverHeader(LandCoverList ls) {
		StringBuffer sb = new StringBuffer();
		sb.append("//LandCover").append(T);
		for (LandCover l : ls) {
			sb.append(l.getLabel()).append(T);
		}
		return sb.toString();
	}

	public static String generateLandCoverMatrix(Zone zone, FileDataType t,
			Object attributes) {
		StringBuffer sb = new StringBuffer();
		LandCoverList lc = zone.getProject().getLandCoverList();
		sb.append(generateLandCoverHeader(lc)).append(R);
		for (LandCover l1 : lc) {
			sb.append(l1.getLabel()).append(T);
			for (LandCover l2 : lc) {
				sb.append(zone.getValue(l1, l2, t.getDataType(), attributes))
						.append(T);
			}
			sb.append(R);
		}

		return sb.toString();
	}

	public static String generateLandCoverMatrixAllZones(Project p,
			FileDataType t, Object attributes) {
		StringBuffer sb = new StringBuffer();
		ZoneList z = p.getZoneList();
		String suffix = "";
		if (attributes != null && attributes instanceof CostBenefitUnit)
			suffix = "_" + ((CostBenefitUnit) attributes).getLabel();
		for (int i = 0; i < z.getSize(); i++) {
			sb.append(t.getLabel() + suffix).append(T).append("ZONE=")
					.append(z.get(i).getLabel()).append(R);
			sb.append(generateLandCoverMatrix(z.get(i), t, attributes));
			sb.append(R);
		}

		return sb.toString();
	}

	public static String generateTPMString(ModelProject mp) {
		StringBuffer sb = new StringBuffer();
		FileDataType t = FileDataType.TRANSITION_PROBABILITY_MATRIX;
		int n = mp.getIterationCount();
		for (int j = 0; j < n; j++) {
			Project p = mp.getSimulatedProject(j);
			ZoneList z = p.getZoneList();
			for (int i = 0; i < z.getSize(); i++) {
				sb.append(t.getLabel()).append(T).append("ITERATION=")
						.append(j).append(T).append("ZONE=")
						.append(z.get(i).getLabel()).append(R);
				sb.append(generateLandCoverMatrix(z.get(i), t, null));
				sb.append(R);
			}
		}
		return sb.toString();
	}

	private static String generateLandcoverZoneTable(Project p, FileDataType t,
			Object attributes) {
		StringBuffer sb = new StringBuffer();
		ZoneList z = p.getZoneList();
		LandCoverList lc = p.getLandCoverList();

		sb.append(generateZoneHeader(z)).append(R);
		for (LandCover l : lc) {
			sb.append(l.getLabel()).append(T);
			for (int i = 0; i < z.getSize(); i++) {

				Double d = null;
				if (t == FileDataType.CARBONSTOCK) {
					d = z.get(i).getCarbonStockList().get(l);
				} else if (t == FileDataType.NPV) {
					if (attributes instanceof CostBenefitUnit)
						d = z.get(i).getNPVList((CostBenefitUnit) attributes)
								.get(l);
				}
				if (d == null) {
					sb.append(0).append(T);
				} else {
					sb.append(d).append(T);
				}
			}
			sb.append(R);
		}
		return sb.toString();
	}

	public static String generateProjectString(Project p) {
		StringBuffer sb = new StringBuffer();
		sb.append(FileDataType.GENERAL.getLabel()).append(R);
		sb.append("file_version=").append(fileVersion1).append(R);
		sb.append("title=").append(p.getLabel()).append(R);
		sb.append("description=").append(p.getDescription()).append(R);
		sb.append("numberofzones=").append(p.getNumberOfZone()).append(R);
		sb.append("total_area=").append(p.getTotalArea()).append(R);
		sb.append("time=").append(p.getDurationTime()).append(R);
		sb.append("include_bg=").append(p.isIncludeBelowgroundEmission())
				.append(R);
		sb.append("include_modif=").append(p.isIncludeModifiedEmission())
		.append(R);
		sb.append("using_bg_factor=").append(p.isUsingBelowgroundFactor())
		.append(R);
		sb.append("using_modif_factor=").append(p.isUsingModifiedFactor())
		.append(R);
		
		ModelProject mp = p.getModelProject();
		int model = 0;
		if(mp != null) {
			model = mp.getIterationCount();
		}
		sb.append("model_iteration=").append(model).append(R);
		sb.append(R);
		sb.append(FileDataType.ZONE.getLabel()).append(R);
		ZoneList z = p.getZoneList();
		for (int i = 0; i < p.getNumberOfZone(); i++) {
			sb.append(z.get(i).getLabel()).append(T);
			sb.append(z.get(i).getAreaFraction()).append(T);
			sb.append(z.get(i).isEligible()).append(T);
			sb.append(z.get(i).getDescription()).append(R);
		}
		sb.append(R);

		sb.append(FileDataType.LANDCOVER.getLabel()).append(R);
		for (LandCover l : p.getLandCoverList()) {
			sb.append(l.getLabel()).append(T);
			sb.append(l.getDescription()).append(R);
		}
		sb.append(R);

		sb.append(FileDataType.ELIGIBILITY.getLabel()).append(R);
		LandCoverList lc = p.getLandCoverList();
		sb.append(generateLandCoverHeader(lc)).append(R);
		for (LandCover l1 : lc) {
			sb.append(l1.getLabel()).append(T);
			for (LandCover l2 : lc) {
				sb.append(p.isEligible(l1, l2)).append(T);
			}
			sb.append(R);
		}
		sb.append(R);

		sb.append(FileDataType.COSTBENEFIT_UNIT.getLabel()).append(R);
		for (CostBenefitUnit l : p.getCostBenefitUnitList()) {
			sb.append(l.getLabel()).append(T);
			sb.append(l.getDescription()).append(R);
		}
		sb.append(R);

		sb.append(FileDataType.CARBONSTOCK.getLabel()).append(R);
		sb.append(generateLandcoverZoneTable(p, FileDataType.CARBONSTOCK, null));
		sb.append(R);

		for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
			sb.append(FileDataType.NPV.getLabel() + "_" + cbu.getLabel())
					.append(R);
			sb.append(generateLandcoverZoneTable(p, FileDataType.NPV, cbu));
			sb.append(R);
		}

		for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
			sb.append(generateLandCoverMatrixAllZones(p,
					FileDataType.COSTBENEFIT_CONVERSION, cbu));
			sb.append(R);
		}

		sb.append(generateLandCoverMatrixAllZones(p,
				FileDataType.LANDCOVER_CHANGE, null));
		sb.append(R);

		sb.append(generateLandCoverMatrixAllZones(p,
				FileDataType.BELOWGROUND_EMISSION, null));
		sb.append(R);
		
		sb.append(generateLandCoverMatrixAllZones(p,
				FileDataType.BELOWGROUND_E_FACTOR, null));
		sb.append(R);

		sb.append(generateLandCoverMatrixAllZones(p,
				FileDataType.MODIF_EMISSION, null));
		sb.append(R);
		
		sb.append(generateLandCoverMatrixAllZones(p,
				FileDataType.MODIF_E_FACTOR, null));
		sb.append(R);

		ModelProject mProj = p.getModelProject();
		if (mProj != null)
			sb.append(generateTPMString(mProj));
		sb.append(R);
		return sb.toString();
	}

	private static String generateZoneHeader(ZoneList z) {
		StringBuffer sb = new StringBuffer();
		sb.append("//LandCover").append(T);
		for (int i = 0; i < z.getSize(); i++) {
			sb.append(z.get(i).getLabel()).append(T);
		}
		return sb.toString();
	}

	public static Project loadData(Reader dataReader) throws IOException {

		BufferedReader in = new BufferedReader(dataReader);
		//Reader in = new InputStreamReader(new FileInputStream("anu"), "UTF-8"); 
		
		// FileReader in = new FileReader(f);
		// LineNumberReader lineIn = new LineNumberReader(in);
		//LineNumberReader lineIn = new LineNumberReader(dataReader);
		String module = null;
		HashMap<String, ArrayList<String>> data = new HashMap<String, ArrayList<String>>();
		ArrayList<String> strList = null;
		
		String str;
		String s;
		//while ((str = lineIn.readLine()) != null) {
		while ((s = in.readLine()) != null) {
			str = new String(s.getBytes(),"UTF-8");
			str = str.trim();
			if (str.startsWith("//"))
				continue;
			if (str.startsWith("#")) {
				if ((module != null) && (strList != null)) {
					data.put(module, strList);
				}
				module = checkLabel(str);
				if (module != null) {
					strList = new ArrayList<String>();
				}
			} else {
				if ((module != null) && (!str.isEmpty()) && (strList != null)) {
					strList.add(str);
				}
			}
		}
		if ((module != null) && (strList != null)) {
			data.put(module, strList);
		}

		//lineIn.close();
		 in.close();
		return storeData(data);

	}

	private static void putEligibilityMatrix(ArrayList<String> strList,
			Project p) {
		if (strList == null)
			return;
		LandCoverList lcList = p.getLandCoverList();
		for (String s : strList) {
			String[] str = s.split(T);
			LandCover lc = lcList.findLandCover(str[0]);
			if (lc != null) {
				for (int i = 0; i < Math.min(lcList.size(), str.length - 1); i++) {
					boolean b = Boolean.parseBoolean(str[i + 1]);
					p.setEligibility(b, lc, lcList.get(i));

				}
			}
		}
	}

	private static void putLandCoverMatrix(ArrayList<String> strList,
			String key, FileDataType type, Project p, Object attributes) {
		DecimalFormat formatter = (DecimalFormat) NumberFormat
		.getNumberInstance(Locale.US);
		String k[] = key.split(T);
		Zone z = null;
		if(type == FileDataType.TRANSITION_PROBABILITY_MATRIX) {
			String iLabel[] = k[1].split(E);
			ModelProject mp = p.getModelProject();
			try {
				int itr = formatter.parse(iLabel[1]).intValue();
				Project sp = mp.getSimulatedProject(itr);
				String zLabel[] = k[2].split(E);
				ZoneList zl = sp.getZoneList();
				z = zl.findZone(zLabel[1]);
			} catch (ParseException e) {
			}
			
		} else {
			String zLabel[] = k[1].split(E);
			ZoneList zl = p.getZoneList();
			z = zl.findZone(zLabel[1]);
		}
		if (z == null)
			return;
		
		LandCoverList lcList = p.getLandCoverList();
		for (String s : strList) {
			String[] str = s.split(T);
			LandCover lc = lcList.findLandCover(str[0]);
			if (lc != null) {
				for (int i = 0; i < Math.min(lcList.size(), str.length - 1); i++) {
					double d = 0;
					try {
						// d = Double.parseDouble(str[i + 1]);
						d = formatter.parse(str[i + 1]).doubleValue();
					} catch (Exception e) {
					}

					z.setValue(d, lc, lcList.get(i), type.getDataType(),
							attributes);
				}
			}
		}
	}

	private static void putLandCoverValue(ArrayList<String> strList,
			FileDataType type, Project p, Object attributes) {
		if (strList == null)
			return;
		ZoneList zl = p.getZoneList();
		LandCoverList lcList = p.getLandCoverList();
		DecimalFormat formatter = (DecimalFormat) NumberFormat
				.getNumberInstance(Locale.US);
		for (String s : strList) {
			String[] str = s.split(T);
			LandCover lc = lcList.findLandCover(str[0]);
			if (lc != null) {
				for (int i = 0; i < Math.min(zl.getSize(), str.length - 1); i++) {
					double d = 0;
					try {
						// d = Double.parseDouble(str[i + 1]);
						d = formatter.parse(str[i + 1]).doubleValue();
					} catch (Exception e) {
					}

					if (type == FileDataType.CARBONSTOCK) {
						zl.get(i).getCarbonStockList().put(lc, d);
					} else if (type == FileDataType.NPV) {
						if (attributes instanceof CostBenefitUnit)
//							zl.get(i).setNPV(d, lc, 
//									(CostBenefitUnit) attributes);
							zl.get(i).setValue(d, lc, DataType.NPV,
								(CostBenefitUnit) attributes);
					}
				}
			}
		}
	}

	public static void saveCalculationOutput(Project p, File file) {
		String str = generateCalculationOutputString(p);
		saveStringData(str, file);
	}

	public static void saveCalculationOutputToExcel(Project p, File file)
			throws WriteException, IOException, BiffException {
		String[][] str = new String[4][2];
		ZoneList z = p.getZoneList();
		str = new String[z.getSize()][2];

		for (FileDataType t : outputData) {
			String s = "";
			if (t == FileDataType.COSTBENEFIT_TOTAL || t == FileDataType.OPCOST) {
				for (CostBenefitUnit cbu : p.getCostBenefitUnitList())
					s = generateLandCoverMatrixAllZones(p, t, cbu);
			} else {
				s = generateLandCoverMatrixAllZones(p, t, null);
			}
			str = convertStringToMatrix(s);
			FileExcelIO.writeArray(str, file, t.getLabel(), 0, 0,
					FileExcelIO.WRITE_REPLACE);
		}

	}

	public static void saveProject(Project p, File file) {
		String str = generateProjectString(p);
		saveStringData(str, file);
	}

	public static void saveProjectToExcel(Project p, File file)
			throws WriteException, IOException, BiffException {
		String[][] str = new String[4][2];
		str[0][0] = "title";
		str[0][1] = p.getLabel();
		str[1][0] = "description";
		str[1][1] = p.getDescription();
		str[2][0] = "numberofzones";
		str[2][1] = String.valueOf(p.getNumberOfZone());
		str[3][0] = "total_area";
		str[3][1] = String.valueOf(p.getTotalArea());
		FileExcelIO.writeArray(str, file, FileDataType.GENERAL.getLabel(), 0,
				0, FileExcelIO.WRITE_REPLACE);

		ZoneList z = p.getZoneList();
		str = new String[z.getSize()][2];
		for (int i = 0; i < p.getNumberOfZone(); i++) {
			str[i][0] = z.get(i).getLabel();
			str[i][1] = String.valueOf(z.get(i).getAreaFraction());
			str[i][2] = String.valueOf(z.get(i).isEligible());
			str[i][3] = z.get(i).getDescription();
		}
		FileExcelIO.writeArray(str, file, FileDataType.ZONE.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);

		str = new String[p.getLandCoverList().size()][1];
		for (int i = 0; i < p.getLandCoverList().size(); i++) {
			str[i][0] = p.getLandCoverList().get(i).getLabel();
		}
		FileExcelIO.writeArray(str, file, FileDataType.LANDCOVER.getLabel(), 0,
				0, FileExcelIO.WRITE_REPLACE);

		String s = generateLandcoverZoneTable(p, FileDataType.CARBONSTOCK, null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file, FileDataType.CARBONSTOCK.getLabel(),
				0, 0, FileExcelIO.WRITE_REPLACE);

		for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
			s = generateLandcoverZoneTable(p, FileDataType.NPV, cbu);
			str = convertStringToMatrix(s);
			FileExcelIO.writeArray(str, file, FileDataType.NPV.getLabel() + "_"
					+ cbu.getLabel(), 0, 0, FileExcelIO.WRITE_REPLACE);
		}

		for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
			s = generateLandCoverMatrixAllZones(p,
					FileDataType.COSTBENEFIT_CONVERSION, cbu);
			str = convertStringToMatrix(s);
			FileExcelIO.writeArray(
					str,
					file,
					FileDataType.COSTBENEFIT_CONVERSION.getLabel() + "_"
							+ cbu.getLabel(), 0, 0, FileExcelIO.WRITE_REPLACE);
		}


		s = generateLandCoverMatrixAllZones(p, FileDataType.LANDCOVER_CHANGE,
				null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file,
				FileDataType.LANDCOVER_CHANGE.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);

		s = generateLandCoverMatrixAllZones(p,
				FileDataType.BELOWGROUND_EMISSION, null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file,
				FileDataType.BELOWGROUND_EMISSION.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);
		
		s = generateLandCoverMatrixAllZones(p,
				FileDataType.BELOWGROUND_E_FACTOR, null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file,
				FileDataType.BELOWGROUND_E_FACTOR.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);
		
		s = generateLandCoverMatrixAllZones(p,
				FileDataType.MODIF_EMISSION, null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file,
				FileDataType.MODIF_EMISSION.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);
		
		s = generateLandCoverMatrixAllZones(p,
				FileDataType.MODIF_E_FACTOR, null);
		str = convertStringToMatrix(s);
		FileExcelIO.writeArray(str, file,
				FileDataType.MODIF_E_FACTOR.getLabel(), 0, 0,
				FileExcelIO.WRITE_REPLACE);
	}

	public static void saveStringData(String data, File file) {
		if ((data == null) || (file == null))
			return;
		try {
//			FileWriter writer = new FileWriter(file);
//			writer.write(data);
//			writer.flush();
//			writer.close();
			Writer out = new BufferedWriter(new OutputStreamWriter(
				    new FileOutputStream(file), "UTF-8"));
				try {
				    out.write(data);
				} finally {
				    out.close();
				}

		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}

	private static Project storeData(HashMap<String, ArrayList<String>> data) {
		Project p = new Project(0);
		String fileVersion = fileVersion0;
		int model_iteration = 0;
		DecimalFormat formatter = (DecimalFormat) NumberFormat
				.getNumberInstance(Locale.US);
		// **** general setting
		ArrayList<String> strList = data.get(FileDataType.GENERAL.getLabel());
		for (String s : strList) {
			String str[] = s.split(E);
			if (str[0].equalsIgnoreCase("title")) {
				if (str.length > 1)
					p.setLabel(str[1]);
			} else if (str[0].equalsIgnoreCase("description")) {
				if (str.length > 1)
					p.setDescription(str[1]);
			} else if (str[0].equalsIgnoreCase("numberofzones")) {
				int n = 0;
				try {
					if (str.length > 1)
						n = Integer.parseInt(str[1]);
					p.setNumberOfZone(n);
				} catch (NumberFormatException e) {
					// TODO: klo eror, liat di zone list aja
				}
			} else if (str[0].equalsIgnoreCase("total_area")) {
				double d = 0;
				try {
					if (str.length > 1)
						d = Double.parseDouble(str[1]);
					d = formatter.parse(str[1]).doubleValue();
				} catch (Exception e) {
				}
				p.setTotalArea(d);
			} else if (str[0].equalsIgnoreCase("time")) {
				int d = 0;
				try {
					if (str.length > 1)
						// d = Double.parseDouble(str[1]);
						d = formatter.parse(str[1]).intValue();
				} catch (Exception e) {
				}
				p.setTime(d);
			} else if (str[0].equalsIgnoreCase("include_bg")) {
				boolean b = false;
				try {
					if (str.length > 1)
						b = Boolean.parseBoolean(str[1]);
				} catch (Exception e) {
				}
				p.setIncludeBelowgroundEmission(b);
			} else if (str[0].equalsIgnoreCase("include_modif")) {
				boolean b = false;
				try {
					if (str.length > 1)
						b = Boolean.parseBoolean(str[1]);
				} catch (Exception e) {
				}
				p.setIncludeModifiedEmission(b);
	
				//******
			} else if (str[0].equalsIgnoreCase("using_bg_factor")) {
				boolean b = false;
				try {
					if (str.length > 1)
						b = Boolean.parseBoolean(str[1]);
				} catch (Exception e) {
				}
				p.setUsingBelowgroundFactor(b);
			} else if (str[0].equalsIgnoreCase("using_modif_factor")) {
				boolean b = false;
				try {
					if (str.length > 1)
						b = Boolean.parseBoolean(str[1]);
				} catch (Exception e) {
				}
				p.setUsingModifiedFactor(b);
			//**	
				
			} else if (str[0].equalsIgnoreCase("file_version")) {
				if (str.length > 1)
					fileVersion = str[1];
			} else if (str[0].equalsIgnoreCase("model_iteration")) {
				int d = 0;
				try {
					if (str.length > 1)
						d = formatter.parse(str[1]).intValue();
				} catch (Exception e) {
				}
					model_iteration = d;
			}
		}
		data.remove(FileDataType.GENERAL.getLabel());

		// **** zones setting
		strList = data.get(FileDataType.ZONE.getLabel());
		if (p.getNumberOfZone() == 0) {
			p.setNumberOfZone(strList.size());
		}
		ZoneList zl = p.getZoneList();
		for (int i = 0; i < Math.min(zl.getSize(), strList.size()); i++) {
			String s[] = strList.get(i).split("[" + T + E + "]");
			zl.get(i).setLabel(s[0].trim());
			double d = 0;
			try {
				if (s.length > 1)
					d = Double.parseDouble(s[1]);
			} catch (Exception e) {
			}
			zl.get(i).setAreaRatio(d);

			boolean b = true;
			try {
				if (s.length > 2)
					b = Boolean.parseBoolean(s[2]);
			} catch (Exception e) {
			}
			zl.get(i).setEligible(b);
			if (s.length > 3) {
				zl.get(i).setDescription(s[3]);
			}
		}
		data.remove(FileDataType.ZONE.getLabel());

		// **** land cover list
		strList = data.get(FileDataType.LANDCOVER.getLabel());
		LandCoverList lcList = p.getLandCoverList();
		lcList.clear();
		ArrayList<LandCover> noteli = new ArrayList<LandCover>();
		for (String s : strList) {
			String str[] = s.split(T);
			LandCover lc = new LandCover(str[0]);
			if (fileVersion.equals(fileVersion0)) {
				boolean b = true;
				try {
					if (str.length > 1)
						b = Boolean.parseBoolean(str[1]);
				} catch (Exception e) {
				}
				if (!b)
					noteli.add(lc);
				// lc.setEligible(b);
			} else if (fileVersion.equals(fileVersion1)) {
				if (str.length > 1)
					lc.setDescription(str[1]);
			}
			lcList.add(lc);
		}
		p.setLandCoverList(lcList);
		data.remove(FileDataType.LANDCOVER.getLabel());

		// **** eligibility
		if (fileVersion.equals(fileVersion0)) {
			if (noteli.size() > 0) {
				LandCoverList lcAll = p.getLandCoverList();
				for (LandCover lc : lcAll) {
					for (LandCover lcFalse : noteli) {
						p.setEligibility(false, lc, lcFalse);
						p.setEligibility(false, lcFalse, lc);
					}
				}
			}
		} else if (fileVersion.equals(fileVersion1)) {
			strList = data.get(FileDataType.ELIGIBILITY.getLabel());
			putEligibilityMatrix(strList, p);
		}

		// **** carbon stock
		strList = data.get(FileDataType.CARBONSTOCK.getLabel());
		putLandCoverValue(strList, FileDataType.CARBONSTOCK, p, null);
		data.remove(FileDataType.CARBONSTOCK.getLabel());

		// **** cost benefit unit
		if (fileVersion.equals(fileVersion0)) {
			ArrayList<CostBenefitUnit> cbuList = p.getCostBenefitUnitList();
			cbuList.add(Project.defaultCostBenefitUnit2);
		} else if (fileVersion.equals(fileVersion1)) {
			strList = data.get(FileDataType.COSTBENEFIT_UNIT.getLabel());
			ArrayList<CostBenefitUnit> cbuList = p.getCostBenefitUnitList();
			cbuList.clear();
			for (String s : strList) {
				String str[] = s.split(T);
				CostBenefitUnit cbu = new CostBenefitUnit(str[0], str[1]);
				cbuList.add(cbu);
			}
			p.setCostBenefitUnitList(cbuList);
			data.remove(FileDataType.COSTBENEFIT_UNIT.getLabel());
		}

		// **** NPV
		ArrayList<CostBenefitUnit> cbuList = p.getCostBenefitUnitList();
		if (fileVersion.equals(fileVersion0)) {
			strList = data.get("#NPV_PRIVATE");
			putLandCoverValue(strList, FileDataType.NPV, p, cbuList.get(0));
			data.remove("#NPV_PRIVATE");

			strList = data.get("#NPV_SOCIAL");
			putLandCoverValue(strList, FileDataType.NPV, p, cbuList.get(1));
			data.remove("#NPV_SOCIAL");
		} else if (fileVersion.equals(fileVersion1)) {
			for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
				strList = data.get(FileDataType.NPV.getLabel() + "_"
						+ cbu.getLabel());
				putLandCoverValue(strList, FileDataType.NPV, p, cbu);
				data.remove(FileDataType.NPV.getLabel());
			}
		}
		if(model_iteration > 0) {
			p.generateSimulationModel(model_iteration);
		}
		Set<String> keys = data.keySet();
		for (String k : keys) {
			strList = data.get(k);
			if (fileVersion.equals(fileVersion0)) {
				if (k.startsWith("#COSTB_PRIVATE")) {
					putLandCoverMatrix(strList, k,
							FileDataType.COSTBENEFIT_CONVERSION, p,
							cbuList.get(0));
				} else if (k.startsWith("#COSTB_SOCIAL")) {
					putLandCoverMatrix(strList, k,
							FileDataType.COSTBENEFIT_CONVERSION, p,
							cbuList.get(1));
				} else if (k.startsWith(FileDataType.LANDCOVER_CHANGE
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.LANDCOVER_CHANGE, p, null);
				} else if (k.startsWith(FileDataType.BELOWGROUND_EMISSION
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.BELOWGROUND_EMISSION, p, null);
				}
			} else if (fileVersion.equals(fileVersion1)) {
				if (k.startsWith(FileDataType.LANDCOVER_CHANGE.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.LANDCOVER_CHANGE, p, null);
				} else if (k.startsWith(FileDataType.BELOWGROUND_EMISSION
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.BELOWGROUND_EMISSION, p, null);
				} else if (k.startsWith(FileDataType.BELOWGROUND_E_FACTOR
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.BELOWGROUND_E_FACTOR, p, null);
				} else if (k.startsWith(FileDataType.MODIF_EMISSION
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.MODIF_EMISSION, p, null);
				} else if (k.startsWith(FileDataType.MODIF_E_FACTOR
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.MODIF_E_FACTOR, p, null);
					
				} else if (k.startsWith(FileDataType.TRANSITION_PROBABILITY_MATRIX
						.getLabel())) {
					putLandCoverMatrix(strList, k,
							FileDataType.TRANSITION_PROBABILITY_MATRIX, p, null);
				} else if (k.startsWith(FileDataType.COSTBENEFIT_CONVERSION
						.getLabel())) {
					for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
						if (k.startsWith(FileDataType.COSTBENEFIT_CONVERSION
								.getLabel() + "_" + cbu.getLabel()))
							putLandCoverMatrix(strList, k,
									FileDataType.COSTBENEFIT_CONVERSION, p, cbu);
					}
				}
			}
		}
		
		if(model_iteration > 0) {
			ModelProject mpo = p.getModelProject();
			Project sibP[] = new Project[model_iteration];
			for(int m = 0; m < model_iteration; m++) {
				sibP[m] = generateSibling(mpo.getSimulatedProject(m));
			}
			
			p.generateSimulationModel(model_iteration);
			
			for(int m = 0; m < model_iteration; m++) {
				ModelProject mp = p.getModelProject();
				Project sp = mp.getSimulatedProject(m);
				ZoneList zo = sibP[m].getZoneList();
				ZoneList zList = sp.getZoneList();
				for (int i = 0; i < zList.getSize(); i++) {
					for (LandCover lc1 : lcList) {
						for (LandCover lc2 : lcList) {
							
								double d = zo.get(i).getValue(lc1, lc2, TPM_SCENARIO, null);
								zList.get(i).setValue(d, lc1, lc2, TPM_SCENARIO, null);
							
						}
					}
				}
				
				mp.runSimulationModel(m, sp);
				
			}
		}
		return p;
	}

	public static Project generateSibling(Project po) {
		Project p = new Project();

		p.setLabel(po.getLabel());
		p.setDescription(po.getDescription());
		p.setNumberOfZone(po.getNumberOfZone());
		p.setTotalArea(po.getTotalArea());
		p.setTime((int) po.getDurationTime());

		p.setIncludeBelowgroundEmission(po.isIncludeBelowgroundEmission());
		p.setIncludeModifiedEmission(po.isIncludeModifiedEmission());
		p.setUsingBelowgroundFactor(po.isUsingBelowgroundFactor());
		p.setUsingModifiedFactor(po.isUsingModifiedFactor());
		
		// **** zones setting
		ZoneList zo = po.getZoneList();
		ZoneList zList = p.getZoneList();
		for (int i = 0; i < zo.getSize(); i++) {
			zList.get(i).setLabel(zo.get(i).getLabel());
			zList.get(i).setAreaRatio(zo.get(i).getAreaFraction());
			zList.get(i).setEligible(zo.get(i).isEligible());
			zList.get(i).setDescription(zo.get(i).getDescription());
		}

		// **** land cover list
		// TODO: apakah LC bisa disamakan??
		LandCoverList lcList = po.getLandCoverList();
		p.setLandCoverList(lcList);
		// **** eligibility
		for (LandCover lc1 : lcList) {
			for (LandCover lc2 : lcList) {
				p.setEligibility(po.isEligible(lc1, lc2), lc1, lc2);
			}
		}

		p.setCostBenefitUnitList(po.getCostBenefitUnitList());

		DataType mType[] = { COSTBENEFIT_CONVERSION, LANDCOVER_CHANGE,
				BELOWGROUND_EMISSION, DataType.BG_EMISSION_FACTOR, DataType.MODIF_EMISSION, DataType.MODIF_EMISSION_FACTOR, TPM_SCENARIO,
				LANDUSE_SCENARIO };
		for (DataType t : mType) {
			for (int i = 0; i < zList.getSize(); i++) {
				for (LandCover lc1 : lcList) {
					for (LandCover lc2 : lcList) {
						if (t == COSTBENEFIT_CONVERSION) {
							for (CostBenefitUnit cbu : p
									.getCostBenefitUnitList()) {
								double d = zo.get(i).getValue(lc1, lc2, t, cbu);
								zList.get(i).setValue(d, lc1, lc2, t, cbu);
							}
						} else {
							double d = zo.get(i).getValue(lc1, lc2, t, null);
							zList.get(i).setValue(d, lc1, lc2, t, null);
						}
					}
				}
			}
		}

		DataType lType[] = { CARBONSTOCK, NPV };
		for (DataType t : lType) {
			for (int i = 0; i < zList.getSize(); i++) {
				for (LandCover lc1 : lcList) {
					if (t == NPV) {
						for (CostBenefitUnit cbu : p.getCostBenefitUnitList()) {
							double d = zo.get(i).getValue(lc1, t, cbu);
							zList.get(i).setValue(d, lc1, t, cbu);
						}
					} else {
						double d = zo.get(i).getValue(lc1, t, null);
						zList.get(i).setValue(d, lc1, t, null);
					}
				}
			}
		}
		return p;
	}
	
	public static String[] loadTableHeader(Reader dataReader) throws IOException {
		BufferedReader in = new BufferedReader(dataReader);
		String header = new String(in.readLine().getBytes(),"UTF-8");
		header = header.trim();
		String[] str = header.split(T);
		in.close();
		return str;
	}
	
	public static HashMap<String, HashMap<String, HashMap<String, Double>>> loadTable(Reader dataReader, int zoneIdx, int lu1Idx, int lu2Idx, int valueIdx, String decimalChar) throws IOException {

		BufferedReader in = new BufferedReader(dataReader);
		
		String header = new String(in.readLine().getBytes(),"UTF-8");
		header = header.trim();
		//String[] str = header.split(T);
		HashMap<String, HashMap<String, HashMap<String, Double>>> data = new HashMap<String, HashMap<String, HashMap<String, Double>>>();
		
		DecimalFormat formatter = (DecimalFormat) NumberFormat
				.getNumberInstance();
		
		if(decimalChar.equals(".")) {
			formatter = (DecimalFormat) NumberFormat
					.getNumberInstance(Locale.US);
		} else if(decimalChar.equals(",")) {
			formatter = (DecimalFormat) NumberFormat
					.getNumberInstance(Locale.GERMAN);
		}
		
		
		String str;
		String s;
		while ((s = in.readLine()) != null) {
			str = new String(s.getBytes(),"UTF-8");
			str = str.trim();
			if (str.startsWith("//"))
				continue;
			String[] row = str.split(T);
			
			HashMap<String, HashMap<String, Double>> dataLU = data.get(row[zoneIdx]);
			if(dataLU == null) {
				dataLU = new HashMap<String, HashMap<String, Double>>();
			}
			HashMap<String, Double> dataLU1 = dataLU.get(row[lu1Idx]);
			if(dataLU1==null) {
				dataLU1 = new HashMap<String, Double>();
			}
			Double val = dataLU1.get(row[lu2Idx]);
			if(val == null) val = 0d;
			try {
				val += formatter.parse(row[valueIdx]).doubleValue();
			} catch (ParseException e) {
				e.printStackTrace();
			}
			dataLU1.put(row[lu2Idx], val);
			dataLU.put(row[lu1Idx], dataLU1);
			data.put(row[zoneIdx], dataLU);
		}
		
		in.close();
		return data;

	}
	
	//base code by alfa
//	public static String[][] importDBF(File file) throws JDBFException {
//		//get file					
//		DBFReader dbfReader = new DBFReader(file.getAbsolutePath());
//		
//		//define variable
//		int dbfFieldCount = dbfReader.getFieldCount();
//		String header[] = new String[dbfFieldCount];
//		//read header
//		for (int i = 0; i < dbfFieldCount; i++) {
//			header[i] = dbfReader.getField(i).getName();
//		}
//		ArrayList<String[]> data = new ArrayList<String[]>();
//		data.add(header);
//		while (dbfReader.hasNextRecord()) {
//			Object aobj[] = dbfReader.nextRecord(Charset.forName("UTF-8"));
//			String[] row = new String[dbfFieldCount];
//			for (int j = 0; j < aobj.length; j++){
//				row[j] = aobj[j].toString();
//			}
//			data.add(row);
//		}
//		
//		String[][] dataStr = (String[][])data.toArray(new String[data.size()][dbfFieldCount]); 
//		return dataStr;
//	}
	
	public static Project importRawData(Project project, String[][] data, int[] inputColumn) throws ParseException {
		DecimalFormat formatter = (DecimalFormat) NumberFormat
				.getNumberInstance(Locale.US);
		HashSet<String> landUses = new HashSet<String>();
		HashMap<String,HashMap<String, HashMap<String, Double>>> lczoneSum = new HashMap<String,HashMap<String, HashMap<String, Double>>>();
		HashMap<String, Double> zones = new HashMap<String, Double>();
		double sumAll = 0;
		
		
		for(int i = 1; i < data.length; i++) {
			String lc1 = data[i][inputColumn[0]];
			String lc2 = data[i][inputColumn[1]];
			String z = data[i][inputColumn[2]];
			if(z.equals("") || lc1.equals("") || lc2.equals("") ) 
				continue;
			
			landUses.add(lc1);
			landUses.add(lc2);
			double d = formatter.parse(data[i][inputColumn[3]]).doubleValue();
			sumAll+=d;
			
			
			Double zd = zones.get(z);
			if(zd == null) {
				zones.put(z, d);
			} else {
				zones.put(z, zd+d);
			}
			
			HashMap<String, HashMap<String, Double>> lcSum = lczoneSum.get(z);
			if(lcSum == null) lcSum = new HashMap<String, HashMap<String, Double>>();
			
			HashMap<String, Double> lc1set = lcSum.get(lc1);
			if(lc1set == null) 	lc1set = new HashMap<String, Double>();
				
			Double lcd = lc1set.get(lc2);
			if(lcd == null) {
				lc1set.put(lc2, d);
			} else {
				lc1set.put(lc2, d+lcd);
			}
			lcSum.put(lc1, lc1set);
			lczoneSum.put(z, lcSum);
		}
		
		
		project.setTotalArea(sumAll);
		
		//set land cover list
		String[] lcArr = landUses.toArray(new String[landUses.size()]);
		Arrays.sort(lcArr);
		LandCoverList lcList = project.getLandCoverList();
		lcList.clear();
		for (int i = 0; i < lcArr.length; i++) {
			lcList.add(new LandCover(lcArr[i]));
		}	
		project.setLandCoverList(lcList);
		
		//set zone list
		String[] zonesArr = zones.keySet().toArray(new String[zones.size()]);
		Arrays.sort(zonesArr);
		project.setNumberOfZone(zonesArr.length);
		ZoneList zl = project.getZoneList();
		for (int i = 0; i < zonesArr.length; i++) {
			zl.get(i).setLabel(zonesArr[i]);
		}
		
		//input land use change matrix data 
		
		ZoneList zList = project.getZoneList();
		for(int i = 0; i < zList.getSize(); i++) {
			String z = zList.get(i).getLabel();
			double zd = zones.get(z);
			Zone zObj = zList.get(i);
			zObj.setAreaRatio(zd/sumAll);
			
			HashMap<String, HashMap<String, Double>> lcSum= lczoneSum.get(z);
			Set<String> lc1set = lcSum.keySet();
			for(String lc1:lc1set) {
				HashMap<String, Double> lc1sum = lcSum.get(lc1);
				Set<String> lc2set = lc1sum.keySet();
				for(String lc2:lc2set) {
					double lcd = lczoneSum.get(z).get(lc1).get(lc2);
					LandCover lc1Obj = lcList.findLandCover(lc1);
					LandCover lc2Obj = lcList.findLandCover(lc2);
					
					zObj.setValue(lcd/zd, lc1Obj, lc2Obj, DataType.LANDCOVER_CHANGE, null);
				}
			}
		}
		
		
		return project;
	}
}
